package pl.drypisy;

import java.awt.image.BufferedImage;

/**
 * 
 * @author Drobny
 * Class performes image rotation with draggable rotation's center
 * At 90 and 180 angles it does simple pixels substitute
 */
public class RotationProcess extends ImageProcess {
    private double angle,angleRadian,angleRadian2,sinVal,cosVal;
    private int imageCenterX,imageCenterY,rotationCenterX, rotationCenterY;
    private BufferedImage src;
    
    public void setParams(int angle, int rCenterX, int rCenterY) {
        this.angle=angle;
        this.angleRadian=((double)(Math.PI*angle)/(double)180.0);
        this.angleRadian2=(double)((double)(Math.PI/2) - angleRadian);
        this.sinVal=Math.sin(angleRadian);
        this.cosVal=Math.cos(angleRadian);
        //this.rotationCenterX=rCenterX;
        //this.rotationCenterY=rCenterY;
    }

    @Override
    public BufferedImage process(BufferedImage image) {
        this.src = image;
        srcWidth = src.getWidth();
        srcHeight = src.getHeight();
        imageCenterX=(int)(src.getWidth()/2);
        imageCenterY=(int)(src.getHeight()/2);
        //rotationCenterX=imageCenterX-rotationCenterX;
        //rotationCenterY=imageCenterY-rotationCenterY;
        if(angle==0){
            return image;
        } else if(angle==90){
            return rotation90();
        } else if(angle==180){
            return rotation180();
        }
        /*
        double p1x=-srcHeight*sinVal;
        double p1y=srcHeight*cosVal;
        double p2x=srcWidth*cosVal - srcHeight*sinVal;
        double p2y=srcHeight*cosVal + srcWidth*sinVal;
        double p3x=srcWidth*cosVal;
        double p3y=srcWidth*sinVal;
        double minX=Math.min(0, Math.min(p1x, Math.min(p2x, p3x)));
        double minY=Math.min(0, Math.min(p1y, Math.min(p2y, p3y)));
        double maxX=Math.max(p1x, Math.max(p2x, p3x));
        double maxY=Math.max(p1y, Math.max(p2y, p3y));
        maxX=Math.abs(maxX);
        maxY=Math.abs(maxY);
        int width=(int) Math.ceil(maxX-minX);
        int height=(int) Math.ceil(maxY-minY);
        System.out.println("maxX: "+maxX+" w: "+ width +"maxY: "+ maxY +" h: "+height);
        */
        int width=(int)(srcHeight * Math.abs(Math.cos(angleRadian2)) + srcWidth * Math.abs(cosVal));
        int height=(int)(srcHeight * Math.abs(cosVal) + srcWidth * Math.abs(Math.cos(angleRadian2)));
        
        int xdiff=(int)((width-srcWidth)/2);
        int ydiff=(int)((height-srcHeight)/2);
        
        //System.out.println("diffx: "+xdiff+" diffy: "+ydiff);
        
        BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        int[] outPixels = new int[width];
        int[] inPixels = src.getRGB(0, 0, srcWidth, srcHeight, null, 0, srcWidth);
        double out[] = new double[2];
        int nw, ne, sw, se;
        
        int indexY=0,index;
        for (int y=-1*ydiff; y<(height-ydiff) ;y++) {
            index=0;
            for (int x=-1*xdiff; x<(width-xdiff) ;x++) {
                getOriginalCoordinates(x,y,out);
                int srcX = (int)Math.floor( out[0] );
                int srcY = (int)Math.floor( out[1] );
                int pix;
                if(doInterpolation) {
                    double xWeight = out[0]-srcX;
                    double yWeight = out[1]-srcY;
                    nw = getPixel( inPixels, srcX, srcY, srcWidth, srcHeight);
                    ne = getPixel( inPixels, srcX+1, srcY, srcWidth, srcHeight );
                    sw = getPixel( inPixels, srcX, srcY+1, srcWidth, srcHeight );
                    se = getPixel( inPixels, srcX+1, srcY+1, srcWidth, srcHeight );
                    pix = bilinearInterpolation(xWeight, yWeight,  nw, ne, sw, se);
                }
                else pix = getPixel(inPixels,srcX,srcY,srcWidth,srcHeight);
                outPixels[index++] = pix;
            }
            dest.setRGB(0, indexY++, width, 1, outPixels, 0, height);
        }
        return dest;
    }


    @Override
    protected void getOriginalCoordinates(int x, int y, double out[]) {
        //out[0]=cosVal*(x-rotationCenterX) + sinVal*(y-rotationCenterY) + rotationCenterX;
        //out[1]=cosVal*(y-rotationCenterY) - sinVal*(x-rotationCenterX) + rotationCenterY;
        out[0]=cosVal*(x-imageCenterX) + sinVal*(y-imageCenterY) + imageCenterX ;
        out[1]=cosVal*(y-imageCenterY) - sinVal*(x-imageCenterX) + imageCenterY ;
    }
    
    /**
     * Simple pixel switch operation for 90 rotation
     * Width of the destination bitmap becomes the Height of the source 
     * and the Height of the destination bitmap, becomes the Width of the source
     * @return destionation image
     */
    private BufferedImage rotation90(){
        int width=srcHeight;
        int height=srcWidth;
        BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        int[] outPixels = new int[width] ;
        int[] inPixels = src.getRGB(0, 0, srcWidth, srcHeight, null, 0, srcWidth);
        
        for(int y=0;y<height;y++) {
            for(int x=0;x<width;x++) {
                int srcX=y;
                int srcY=width-1-x;
                outPixels[x]=getPixel(inPixels,srcX,srcY,srcWidth,srcHeight);
            }
            dest.setRGB(0, y, width, 1, outPixels, 0, height);
        }
        return dest;
    }
    
    /**
     * Simple pixel switch operation for 180 rotation
     * @return destionation image
     */
    private BufferedImage rotation180(){
        int width=srcWidth;
        int height=srcHeight;
        BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        int[] outPixels = new int[width] ;
        int[] inPixels = src.getRGB(0, 0, srcWidth, srcHeight, null, 0, srcWidth);
        for (int y=0;y<height;y++) {
            for (int x=0;x<width;x++) {
                int srcX=x;
                int srcY=height-1-y;
                outPixels[x]=getPixel(inPixels,srcX,srcY,srcWidth,srcHeight);
            }
            dest.setRGB(0, y, width, 1, outPixels, 0, height);
        }
        return dest;
    }
    
}